package cn.zwk.boot;

import cn.zwk.config.ColumnBinding;
import cn.zwk.config.ColumnSelectorConfig;
import cn.zwk.config.ConfigLoader;
import cn.zwk.config.HandlerDef;
import cn.zwk.config.ParseOptionsConfig;
import cn.zwk.config.PipelineConfig;
import cn.zwk.orchestrator.PipelineOrchestrator;
import cn.zwk.orchestrator.model.ReportPayload;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;

@Component
public class StartupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        Map<String, Object> root = loadApplicationYaml();
        Map<String, Object> global = asMap(root.get("global"));
        List<Map<String, Object>> projects = asListOfMap(root.get("project"));

        // Build global handlers by name
        Map<String, HandlerDef> globalHandlers = buildHandlers(asListOfMap(global.get("handler")));

        PipelineOrchestrator orch = new PipelineOrchestrator();

        for (Map<String, Object> proj : projects) {
            Map<String, HandlerDef> handlers = new LinkedHashMap<>(globalHandlers);
            // project-level handlers override
            List<Map<String, Object>> projHandlers = asListOfMap(proj.get("handler"));
            handlers.putAll(buildHandlers(projHandlers));

            // project envs
            Map<String, Object> envs = asMap(proj.get("envs"));

            // parse options
            ParseOptionsConfig parseCfg = new ParseOptionsConfig();
            parseCfg.setHeaderRowIndex(0);
            parseCfg.setSkipMergedCells(true);
            parseCfg.setSkipEmptyKeyRows(true);
            parseCfg.setKeyColumn(parseSelector(proj.get("key-col")));

            // columns from query list
            List<ColumnBinding> columns = new ArrayList<>();
            List<Map<String, Object>> queryList = asListOfMap(proj.get("query"));
            for (Map<String, Object> q : queryList) {
                ColumnBinding cb = new ColumnBinding();
                cb.setSelector(parseSelector(q.get("col")));
                cb.setHandler(String.valueOf(q.get("handler")));
                // per-column envs extension not defined in this config; leave null
                columns.add(cb);
            }

            PipelineConfig pc = new PipelineConfig();
            pc.setParse(parseCfg);
            pc.setHandlers(handlers);
            pc.setColumns(columns);
            pc.setExcelFile(String.valueOf(proj.get("excel-file")));
            pc.setOutputFile(String.valueOf(proj.get("output-file")));
            pc.setOutputTemplateFile(String.valueOf(proj.get("output-template-file")));
            pc.setEnvs(envs);

            ReportPayload payload = orch.execute(pc);
            orch.writeReport(payload, Path.of(pc.getOutputTemplateFile()), Path.of(pc.getOutputFile()));
            System.out.println("完成报告生成: " + pc.getOutputFile());
        }
    }

    private static Map<String, HandlerDef> buildHandlers(List<Map<String, Object>> list) {
        Map<String, HandlerDef> out = new LinkedHashMap<>();
        if (list == null) return out;
        for (Map<String, Object> h : list) {
            String name = String.valueOf(h.get("name"));
            String type = String.valueOf(h.get("type"));
            Map<String, Object> before = asMap(h.get("before-handler"));
            Map<String, Object> after = asMap(h.get("after-ops"));

            Map<String, Object> envs = new LinkedHashMap<>();
            if ("sh".equalsIgnoreCase(type)) {
                // 支持 template 或 template-file
                if (before.containsKey("template-file")) {
                    String path = String.valueOf(before.get("template-file"));
                    envs.put("cmdTemplate", readText(path));
                } else if (before.containsKey("template")) {
                    envs.put("cmdTemplate", String.valueOf(before.get("template")));
                }
                if (before.containsKey("shell")) envs.put("shell", String.valueOf(before.get("shell")));
                if (before.containsKey("timeoutMs")) envs.put("timeoutMs", String.valueOf(before.get("timeoutMs")));
            } else if ("http".equalsIgnoreCase(type)) {
                if (before.containsKey("url")) envs.put("url", String.valueOf(before.get("url")));
                if (before.containsKey("method")) envs.put("method", String.valueOf(before.get("method")));
                if (before.containsKey("headers")) envs.put("headers", before.get("headers"));
                if (before.containsKey("body")) envs.put("body", String.valueOf(before.get("body")));
                if (before.containsKey("timeoutMs")) envs.put("timeoutMs", String.valueOf(before.get("timeoutMs")));
                if (before.containsKey("query")) envs.put("query", before.get("query"));
            }
            if (after.containsKey("status")) envs.put("statusTemplate", String.valueOf(after.get("status")));
            if (after.containsKey("snippet")) envs.put("snippetTemplate", String.valueOf(after.get("snippet")));

            HandlerDef def = new HandlerDef();
            def.setType(type);
            def.setEnvs(envs);
            out.put(name, def);
        }
        return out;
    }

    private static ColumnSelectorConfig parseSelector(Object v) {
        ColumnSelectorConfig c = new ColumnSelectorConfig();
        if (v == null) return c;
        String s = String.valueOf(v).trim();
        try {
            int idx = Integer.parseInt(s);
            c.setIndex(idx);
        } catch (NumberFormatException e) {
            c.setHeaderName(s);
        }
        return c;
    }

    private static Map<String, Object> loadApplicationYaml() {
        try (InputStream in = Objects.requireNonNull(StartupRunner.class.getClassLoader().getResourceAsStream("application.yml"))) {
            Yaml yaml = new Yaml();
            Object root = yaml.load(in);
            return asMap(root);
        } catch (Exception e) {
            throw new RuntimeException("无法读取 application.yml", e);
        }
    }

    @SuppressWarnings("unchecked")
    private static Map<String, Object> asMap(Object o) {
        return o == null ? new LinkedHashMap<>() : (Map<String, Object>) o;
    }

    @SuppressWarnings("unchecked")
    private static List<Map<String, Object>> asListOfMap(Object o) {
        if (o == null) return List.of();
        if (o instanceof List<?> l) {
            List<Map<String, Object>> r = new ArrayList<>();
            for (Object e : l) r.add(asMap(e));
            return r;
        }
        return List.of();
    }

    private static String readText(String path) {
        try {
            return java.nio.file.Files.readString(Path.of(path));
        } catch (Exception e) {
            throw new RuntimeException("读取模板文件失败: " + path, e);
        }
    }
}
